package com.example.domain.valueobject;

import com.example.exception.InvalidRequestException;
import lombok.Data;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * 数量值对象
 * 封装数量值和单位
 * 
 * @author Means
 * @since 2025-10-27
 */
@Data
public class Quantity {
    
    /** 数量值 */
    private final BigDecimal value;
    
    /** 计量单位 */
    private final String unit;
    
    /** 计量单位名称（用于前端显示） */
    private final String unitName;
    
    private Quantity(BigDecimal value, String unit, String unitName) {
        if (value == null) {
            throw new InvalidRequestException("数量值不能为空");
        }
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            throw new InvalidRequestException("数量值不能为负数");
        }
        if (unit == null || unit.trim().isEmpty()) {
            throw new InvalidRequestException("计量单位不能为空");
        }
        if (unitName == null || unitName.trim().isEmpty()) {
            throw new InvalidRequestException("计量单位名称不能为空");
        }
        this.value = value;
        this.unit = unit.trim().toUpperCase();
        this.unitName = unitName;
    }
    
    /**
     * 创建数量对象
     * 
     * @param value 数量值
     * @param unit 计量单位
     * @return 数量对象
     */
    public static Quantity of(BigDecimal value, String unit, String unitName) {
        return new Quantity(value, unit, unitName);
    }
    
    /**
     * 创建数量对象
     * 
     * @param value 数量值（double）
     * @param unit 计量单位
     * @return 数量对象
     */
    public static Quantity of(double value, String unit, String unitName) {
        return new Quantity(BigDecimal.valueOf(value), unit, unitName);
    }
    
    /**
     * 创建数量对象
     * 
     * @param value 数量值（String）
     * @param unit 计量单位
     * @return 数量对象
     */
    public static Quantity of(String value, String unit, String unitName) {
        try {
            return new Quantity(new BigDecimal(value), unit, unitName);
        } catch (NumberFormatException e) {
            throw new InvalidRequestException("无效的数量值: " + value);
        }
    }
    
    /**
     * 零数量
     * 
     * @param unit 计量单位
     * @return 零数量对象
     */
    public static Quantity zero(String unit, String unitName) {
        return new Quantity(BigDecimal.ZERO, unit, unitName);
    }
    
    /**
     * 是否为零
     * 
     * @return 是否为零
     */
    public boolean isZero() {
        return value.compareTo(BigDecimal.ZERO) == 0;
    }
    
    /**
     * 是否为正数
     * 
     * @return 是否为正数
     */
    public boolean isPositive() {
        return value.compareTo(BigDecimal.ZERO) > 0;
    }
    
    /**
     * 加法
     * 
     * @param other 另一个数量
     * @return 相加结果
     */
    public Quantity add(Quantity other) {
        checkUnitCompatibility(other);
        return new Quantity(this.value.add(other.value), this.unit, this.unitName);
    }
    
    /**
     * 减法
     * 
     * @param other 另一个数量
     * @return 相减结果
     */
    public Quantity subtract(Quantity other) {
        checkUnitCompatibility(other);
        BigDecimal result = this.value.subtract(other.value);
        if (result.compareTo(BigDecimal.ZERO) < 0) {
            throw new InvalidRequestException("数量不能为负数");
        }
        return new Quantity(result, this.unit, this.unitName);
    }
    
    /**
     * 乘法（数量 × 倍数）
     * 
     * @param multiplier 乘数
     * @return 相乘结果
     */
    public Quantity multiply(BigDecimal multiplier) {
        if (multiplier.compareTo(BigDecimal.ZERO) < 0) {
            throw new InvalidRequestException("乘数不能为负数");
        }
        return new Quantity(this.value.multiply(multiplier), this.unit, this.unitName);
    }
    
    /**
     * 乘法（数量 × 倍数）
     * 
     * @param multiplier 乘数
     * @return 相乘结果
     */
    public Quantity multiply(double multiplier) {
        return multiply(BigDecimal.valueOf(multiplier));
    }
    
    /**
     * 除法（数量 ÷ 除数）
     * 
     * @param divisor 除数
     * @param scale 保留小数位数
     * @return 相除结果
     */
    public Quantity divide(BigDecimal divisor, int scale) {
        if (divisor.compareTo(BigDecimal.ZERO) == 0) {
            throw new InvalidRequestException("除数不能为零");
        }
        return new Quantity(this.value.divide(divisor, scale, BigDecimal.ROUND_HALF_UP), this.unit, this.unitName);
    }
    
    /**
     * 比较大小
     * 
     * @param other 另一个数量
     * @return 比较结果（-1: 小于, 0: 等于, 1: 大于）
     */
    public int compareTo(Quantity other) {
        checkUnitCompatibility(other);
        return this.value.compareTo(other.value);
    }
    
    /**
     * 是否大于
     * 
     * @param other 另一个数量
     * @return 是否大于
     */
    public boolean greaterThan(Quantity other) {
        return compareTo(other) > 0;
    }
    
    /**
     * 是否小于
     * 
     * @param other 另一个数量
     * @return 是否小于
     */
    public boolean lessThan(Quantity other) {
        return compareTo(other) < 0;
    }
    
    /**
     * 单位换算
     * 
     * @param targetUnit 目标单位
     * @param conversionFactor 换算系数
     * @return 换算后的数量
     */
    public Quantity convertTo(String targetUnit, BigDecimal conversionFactor) {
        if (targetUnit == null || targetUnit.trim().isEmpty()) {
            throw new InvalidRequestException("目标单位不能为空");
        }
        if (conversionFactor == null || conversionFactor.compareTo(BigDecimal.ZERO) <= 0) {
            throw new InvalidRequestException("换算系数必须为正数");
        }
        BigDecimal convertedValue = this.value.multiply(conversionFactor);
        return new Quantity(convertedValue, targetUnit, this.unitName);
    }
    
    /**
     * 检查单位兼容性
     * 
     * @param other 另一个数量
     */
    private void checkUnitCompatibility(Quantity other) {
        if (!this.unit.equals(other.unit)) {
            throw new InvalidRequestException(
                String.format("单位不兼容: %s 和 %s", this.unit, other.unit));
        }
    }
    
    /**
     * 格式化为字符串
     * 
     * @return 格式化字符串
     */
    public String format() {
        return value.stripTrailingZeros().toPlainString() + " " + unitName;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Quantity quantity = (Quantity) o;
        return value.compareTo(quantity.value) == 0 && unit.equals(quantity.unit) && unitName.equals(quantity.unitName);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(value, unit, unitName);
    }
    
    @Override
    public String toString() {
        return format();
    }
}

